Parte 9 - Intro a Programas Encriptados

Lo crean o no, es posible hacer cálculos con datos encriptados. En otras palabras, es posible ejecutar un programa donde TODAS las variables están encriptadas!

En este tutorial, vamos a ir paso a paso por las herramientas más básicas de la computación encriptada. Específicamente, veremos un enfoque popular llamado Computación Segura Multi-Parte (Secure Multi-Party Computation). En esta lección, aprenderemos cómo construir una calculadora encriptada que puede hacer operaciones en números encriptados.

Autores:

Referencias:

Traductores:

Paso 1: Encriptación Usando Computación Segura Multi-Parte (CSMP)

A primera vista, CSMP es una manera extraña de "encriptación". En vez de usar una clave pública/privada para encriptar una variable, cada valor es dividido en múltiples acciones, cada una funcionando como una clave privada. Típicamente, éstas acciones serán distribuidas entre 2 o más dueños. Por lo tanto, para descifrar la variable, todos los dueños deben de estar de acuerdo en permitir el desciframiento. En esencia, cada persona tiene una clave privada.

Encrypt()

Digamos que queremos "encriptar" una variable x, podríamos hacerlo de la siguiente manera.

La encriptación no usa decimales o números reales, sino que ocurre en un espacio matemático llamado anillo cociente, el cual abarca los números enteros entre 0 y Q-1, donde Q es un número primo y "suficientemente grande" para que el espacio pueda contener todos los números que usamos en nuestros experimentos. En la práctica, dado un número entero x, nosotros hacemos x % Q para encajar en el anillo. (Esa es la razón por la que evitamos usar un número x' > Q).


In [1]:
Q = 1234567891011

In [2]:
x = 25

In [3]:
import random

def encrypt(x):
    share_a = random.randint(-Q,Q)
    share_b = random.randint(-Q,Q)
    share_c = (x - share_a - share_b) % Q
    return (share_a, share_b,  share_c)

In [4]:
encrypt(x)


Out[4]:
(-1193682418467, 278092132705, 915590285787)

Como pueden ver aquí, hemos dividido nuestra variable x en 3 diferentes acciones, las cuales pueden ser enviadas a 3 dueños diferentes.

Decrypt()

Si quisiéramos descifrar estas 3 acciones, podríamos simplemente sumarlas y tomar el módulo del resultado (mod Q)


In [5]:
def decrypt(*shares):
    return sum(shares) % Q

In [6]:
a,b,c = encrypt(25)

In [7]:
decrypt(a, b, c)


Out[7]:
25

Nótese que si tratamos de descifrar el valor de x con solo dos acciones, el descifrado no funciona!


In [8]:
decrypt(a, b)


Out[8]:
751701837900

Por lo tanto, necesitamos a todos los dueños para poder hacer el descifrado. De esta manera, las acciones sirven como claves privadas, las cuales tienen que estar presente para descifrar un valor.

Paso 2: Aritmética Básica Utilizando CSMP

Sin embargo, la propiedad de la Computación Segura Multi-Parte que es realmente extraordinaria es la habilidad para hacer computaciones mientras las variables están encriptadas. Vamos a demostrar una simple adición.


In [9]:
x = encrypt(25)
y = encrypt(5)

In [10]:
def add(x, y):
    z = list()
    # el primer trabajador suma sus acciones
    z.append((x[0] + y[0]) % Q)
    
    # el segundo trabajador suma sus acciones
    z.append((x[1] + y[1]) % Q)
    
    # el tercer trabajador suma sus acciones
    z.append((x[2] + y[2]) % Q)
    
    return z

In [11]:
decrypt(*add(x,y))


Out[11]:
30

Éxito!!!

Y aquí lo tienen! Si cada trabajador (de manera separada) suma sus acciones, las acciones resultantes van a descifrar el valor correcto (25 + 5 == 30).

Resulta que existen protocolos de CSMP que permiten esta computación encriptada para las siguientes operaciones:

  • adición (que acabamos de ver)
  • multiplicación
  • comparación

y utilizando estas operaciones primitivas, podemos hacer computaciones arbitrarias!!

En la siguiente sección, vamos a aprender a cómo usar la librería PySyft para hacer tales operaciones!

Paso 3: CSMP Usando PySyft

En las secciones anteriores, describimos la intuición básica sobre como la CSMP debe de funcionar. Sin embargo, en la práctica no queremos escribir todas las operaciones primitivas nosotros cuando escribimos nuestros programas encriptados. En esta sección vamos a aprender lo básico sobre de la computación encriptada usando PySyft. Específicamente, nos vamos a enfocar en cómo ejecutar los 3 primitivos antes mencionados: adición, multiplicación y comparación.

Primero, necesitamos crear unos cuantos Trabajadores Virtuales (con los cuales esperemos que estén ya familiarizados dados nuestros tutoriales previos)


In [12]:
import torch
import syft as sy
hook = sy.TorchHook(torch)

bob = sy.VirtualWorker(hook, id="bob")
alice = sy.VirtualWorker(hook, id="alice")
bill = sy.VirtualWorker(hook, id="bill")

Encripción/Descifrado Básico

La encripción es tan simple como tomar cualquier tensor de PySyft y llamar .share(). El descifrado es tan simple como llamar .get() en la variable compartida.


In [13]:
x = torch.tensor([25])

In [14]:
x


Out[14]:
tensor([25])

In [15]:
encrypted_x = x.share(bob, alice, bill)

In [16]:
encrypted_x.get()


Out[16]:
tensor([25])

Introspección de los Valores Encriptados

Si vemos de cerca a los trabajadores de Bob, Alice y Bill podemos ver las acciones que se crean!


In [17]:
bob._objects


Out[17]:
{}

In [18]:
x = torch.tensor([25]).share(bob, alice, bill)

In [19]:
# Las acciones de Bob
bobs_share = list(bob._objects.values())[0]
bobs_share


Out[19]:
tensor([5951150824205329319])

In [20]:
# Las acciones de Alice
alices_share = list(alice._objects.values())[0]
alices_share


Out[20]:
tensor([4666927154753710192])

In [21]:
# Las acciones de Bill
bills_share = list(bill._objects.values())[0]
bills_share


Out[21]:
tensor([7828666094750512130])

Y si quisiéramos, podríamos descifrar estos valores utilizando EL MISMO enfoque del que hablamos antes!


In [22]:
(bobs_share + alices_share + bills_share)


Out[22]:
tensor([25])

Como pueden ver, cuando llamamos .share(), simplemente partió el valor en 3 acciones y envió una acción a cada una de las partes!

Aritmética Encriptada

Ahora pueden ver que podemos hacer aritmética con los valores! La API está construida de tal manera que podemos hacer aritmética como haríamos con los tensores de Pytorch.


In [23]:
x = torch.tensor([25]).share(bob,alice)
y = torch.tensor([5]).share(bob,alice)

In [24]:
z = x + y
z.get()


Out[24]:
tensor([30])

In [25]:
z = x - y
z.get()


Out[25]:
tensor([20])

Multiplicación Encriptada

Para la multiplicación necesitamos una parte adicional, quien es responsable de generar números aleatorios constantemente (y que no colude con ninguna de las otras partes). Denominamos a esta persona la "proveedora de encripción". Para nuestros propósitos, la proveedora de encripción es solo una VirtualWorker (Trabajadora Virtual) adicional, pero es importante reconocer que la proveedora no es una "dueña", en el sentido que ella no posee acciones propias pero es alguien en quien confiamos que no coludirá con los dueños existentes de las acciones.


In [26]:
crypto_provider = sy.VirtualWorker(hook, id="crypto_provider")

In [27]:
x = torch.tensor([25]).share(bob,alice, crypto_provider=crypto_provider)
y = torch.tensor([5]).share(bob,alice, crypto_provider=crypto_provider)

In [28]:
# multiplicación

z = x * y
z.get()


Out[28]:
tensor([125])

También pueden hacer multiplicación de matrices


In [29]:
x = torch.tensor([[1, 2],[3,4]]).share(bob,alice, crypto_provider=crypto_provider)
y = torch.tensor([[2, 0],[0,2]]).share(bob,alice, crypto_provider=crypto_provider)

In [30]:
# multiplicación de matrices

z = x.mm(y)
z.get()


Out[30]:
tensor([[2, 4],
        [6, 8]])

Comparación Encriptada

Es también posible el hacer comparaciones privadas entre valores privados. En este caso contamos con el protocolo SecureNN, cuyos detalles pueden encontrar aquí. El resultado de la comparación es también un tensor compartido privado.


In [31]:
x = torch.tensor([25]).share(bob,alice, crypto_provider=crypto_provider)
y = torch.tensor([5]).share(bob,alice, crypto_provider=crypto_provider)

In [32]:
z = x > y
z.get()


Out[32]:
tensor([1])

In [33]:
z = x <= y
z.get()


Out[33]:
tensor([0])

In [34]:
z = x == y
z.get()


Out[34]:
tensor([0])

In [35]:
z = x == y + 20
z.get()


Out[35]:
tensor([1])

También pueden hacer operaciones de max.


In [36]:
x = torch.tensor([2, 3, 4, 1]).share(bob,alice, crypto_provider=crypto_provider)
x.max().get()


Out[36]:
tensor([4])

In [37]:
x = torch.tensor([[2, 3], [4, 1]]).share(bob,alice, crypto_provider=crypto_provider)
max_values, max_ids = x.max(dim=0)
max_values.get()


Out[37]:
tensor([4, 3])

!Felicitaciones! - !Es hora de unirte a la comunidad!

¡Felicitaciones por completar esta parte del tutorial! Si te gustó y quieres unirte al movimiento para preservar la privacidad, propiedad descentralizada de IA y la cadena de suministro de IA (datos), puedes hacerlo de las siguientes formas!

Dale una estrella a PySyft en GitHub

La forma más fácil de ayudar a nuestra comunidad es por darle estrellas a los repositorios de Github! Esto ayuda a crear consciencia de las interesantes herramientas que estamos construyendo.

¡Únete a nuestro Slack!

La mejor manera de mantenerte actualizado con los últimos avances es ¡unirte a la comunidad! Tú lo puedes hacer llenando el formulario en http://slack.openmined.org

¡Únete a un proyecto de código!

La mejor manera de contribuir a nuestra comunidad es convertirte en un ¡contribuidor de código! En cualquier momento puedes ir al Github Issues de PySyft y filtrar por "Proyectos". Esto mostrará todos los tiquetes de nivel superior dando un resumen de los proyectos a los que ¡te puedes unir! Si no te quieres unir a un proyecto, pero quieres hacer un poco de código, también puedes mirar más mini-proyectos "de una persona" buscando por Github Issues con la etiqueta "good first issue".

Donar

Si no tienes tiempo para contribuir a nuestra base de código, pero quieres ofrecer tu ayuda, también puedes aportar a nuestro Open Collective". Todas las donaciones van a nuestro web hosting y otros gastos de nuestra comunidad como ¡hackathons y meetups!

OpenMined's Open Collective Page


In [ ]: